#pragma once
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>

/*********   FILE   *********/

//our file descriptions
#define FILEDESC_LAST_FLAG  0x80
#define FILEDESC_TYPE_MASK  0x7F
#define FILEDESC_TYPE_DIR   0x01
#define FILEDESC_TYPE_LINK  0x02
#define FILEDESC_TYPE_CHAR  0x03
#define FILEDESC_TYPE_BLOCK 0x04
#define FILEDESC_TYPE_FIFO  0x05
#define FILEDESC_TYPE_SOCK  0x06
#define FILEDESC_TYPE_FILE  0x07

struct file_db{
	
	//desc[0]  = type
	//&desc[1] = name
	char* desc;
	
	off_t size;
	
	union{
		struct file_db* entries;
		char* info;//TODO: file info
	};
};


/*********   UNIX   *********/

#define PROGDESC_TYPE_READER       1
#define PROGDESC_TYPE_ARCHIVER   ( 1 << 1 )
#define PROGDESC_TYPE_COMPRESSOR ( 1 << 2 )

struct progdesc {
	const char* const name;
	const char* const alias;
	char* path;
	const unsigned char type;
};


/*
struct proglist {
	const struct progdesc* desc;
	char* path;
	struct proglist* next;
};
*/

/*********   MAIN STRUCT   *********/

struct instance{
	
	char** envp;
	char shareId[32];
	
	//path to share
	char* sharePath;
	
	//network
	int clientSocket;
	int netTimeout;
	
	//http
	char method[16];
	char uri[256];
	char protocol[16];
	
	//root file node
	struct file_db root;
	
	//prog list
	//struct proglist* progs;
	struct progdesc* progs;
};


/*********   FUNCTIONS   *********/
//files
int indexShare( struct instance* this );

//html
int shareBrowserHtml( struct instance* this );
int loadNodesBackend( struct instance* this, size_t uriOffset );
int htmlDownloadHandler( struct instance* this, size_t uriOffset );

//malloc
int   customMallocInit( void );
void* customMalloc( size_t count );
void  customFreeAll( void );

//network
int startServer( struct instance* this, unsigned short listenPort );
int httpSendStatus( struct instance* this, int statusCode, char* statusText );

//unix
int createPipe( int* pipeInput, int* pipeOutput );
int searchUnixPrograms( struct instance* this );
pid_t startUnixProgram( char* argv[], char* envp[], int stdio[3] );
int unixProgramResult( pid_t programPid, const char* programName );
int unixDownloadHandler( struct instance* this, struct file_db* requested, char* format );

//utils
int parserCore( const char* str, char split, char* buf, size_t bufsize, size_t* offset );
void readableSize( char* buf, off_t bytes );
int uriEncode( const char* str, char* buf, int bufsize );

/****************************
*       INLINE UTILS        *
****************************/

//return 0 if OK, -1 on error
static inline int sendData( int fd, const void* data, size_t count ){
	
	register ssize_t res = 0;
	register size_t sent = 0;
	
	while( sent < count ){
		res = write( fd, data + sent, count - sent );
		if( res < 0 ){
			if( errno == EAGAIN ) printf( "%s: %s\n", __FUNCTION__, strerror(ETIMEDOUT) );
			else perror( __FUNCTION__ );
			return -1;
		}
		sent += res;
	}
	
	return 0;
}

static inline int convertStatType( struct stat* statbuf, char* type ){
	
	if(      (statbuf->st_mode & S_IFMT) == S_IFDIR  ) *type = FILEDESC_TYPE_DIR;
	else if( (statbuf->st_mode & S_IFMT) == S_IFREG  ) *type = FILEDESC_TYPE_FILE;
	else if( (statbuf->st_mode & S_IFMT) == S_IFLNK  ) *type = FILEDESC_TYPE_LINK;
	else if( (statbuf->st_mode & S_IFMT) == S_IFCHR  ) *type = FILEDESC_TYPE_CHAR;
	else if( (statbuf->st_mode & S_IFMT) == S_IFBLK  ) *type = FILEDESC_TYPE_BLOCK;
	else if( (statbuf->st_mode & S_IFMT) == S_IFIFO  ) *type = FILEDESC_TYPE_FIFO;
	else if( (statbuf->st_mode & S_IFMT) == S_IFSOCK ) *type = FILEDESC_TYPE_SOCK;
	else{
		printf( "Stat return unknown type.\n");
		return -1;
	}
	return 0;
}
